#ifndef PUDGE_META_MONAD_TEST_HPP
#define PUDGE_META_MONAD_TEST_HPP

#include <pudge/meta/monad.hpp>

namespace pudge { namespace meta {

	struct monadic_test
	{
		struct my_type; 

		typedef std::vector<my_type*> List; 
		typedef List::iterator Iterator;
		typedef pudge::meta::monadic<int (int),Iterator> Monad;

		struct my_type : Monad::derived<my_type>
		{ 
			template<class T> struct result { typedef T type; };

			int operator()(int i)
			{
				return i + 1; 
			}
		};

		void operator()()
		{
			List list_; 
			list_.push_back(new my_type());
			Monad::compose composite(list_.begin(), list_.end());

			int i = composite(1);

			if(i != 2) throw -1;
		}
	};

	struct monad_test_1_
	{	
		struct function_1
		{
			template<class R> struct result { typedef int type; };

			int operator()(int x)
			{
				return x + 1;
			}
		};
		struct function_2
		{
			template<class R> struct result { typedef int type; };

			int operator ()(int x)
			{
				return x + 2;
			}
		};
		void operator ()()
		{
			typedef monad<int (int)> M;

			M monad_;
			monad_ += function_1();
			monad_ += function_2();

			int x = monad_(1);

			if(x != 4) throw -1;
		}
	} monad_test_1;

	struct monad_test_2_
	{
		struct base { int x; };
		struct derived : base { };

		struct function_1
		{
			template<class T> struct result { typedef derived type; };

			derived operator ()(base & b)
			{
				derived d;
				d.x = b.x + 56;
				return d;
			}
		};
		struct function_2
		{
			template<class T> struct result { typedef base type; };

			base operator ()(base & b)
			{
				base r;
				r.x = b.x + 1;
				return r;
			}
		};
		void operator()()
		{
			typedef monad<base (base)> M;

			M monad_;

			monad_ += function_1();
			monad_ += function_2();

			base b;
			b.x = 0;

			b = monad_(b);

			if(b.x != 57) throw -1;
		}
	} monad_test_2;

	struct monad_test_3_
	{
		struct function_1
		{
			template<class T> struct result{ typedef int& type; };
			int& operator()(int& i)
			{
				return i+=3;
			}
		};
		struct function_2
		{
			template<class T> struct result{ typedef int& type; };
			int& operator()(int& i)
			{
				return i+=2;
			}
		};
		void operator()() const
		{
			typedef monad<int& (int&)> monad_type;

			monad_type monad_;

			monad_ += function_1();
			monad_ += function_2();

			int x = 0;

			int & y = monad_(x);

			if(y != 5) throw -1;
		}
	} const monad_test_3;

	struct monad_test_4_
	{
		struct function_1
		{
			template<class T1,class T2> struct result { typedef int type; };
			int operator ()(int& i1, int i2)
			{
				return i1 + i2;
			}
		};
		struct function_2
		{
			template<class T1,class T2> struct result { typedef int& type; };
			int& operator ()(int& i1, int i2)
			{
				return i1 *= i2;
			}
		};
		void operator ()() const
		{
			typedef monad<int (int&, int)> monad_type;

			monad_type monad_;

			monad_ += function_2();
			monad_ += function_1();

			int x = 1;

			int y = monad_(x,2);

			if(y!= 4) throw -1;
			if(x != 2) throw -1;
		}
	} const monad_test_4;

	void monad_test()
	{
		monad_test_1();
		monad_test_2();
		monad_test_3();
		monad_test_4();
	}
}}

#endif